package cn.mju.tankfighterbackend.service.game

import cn.mju.tankfighterbackend.dto.game.GameData
import cn.mju.tankfighterbackend.dto.game.SyncFrameDto
import cn.mju.tankfighterbackend.entity.game.Game
import cn.mju.tankfighterbackend.util.JsonUtil
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.web.socket.*
import org.springframework.web.socket.handler.TextWebSocketHandler
import java.util.concurrent.ConcurrentHashMap

@Service
class GameServer: TextWebSocketHandler() {

    //玩家与所在的游戏<username,gameId>
    val playerInGame:MutableMap<String,Long> = ConcurrentHashMap()
    val games:MutableMap<Long, GameExecutor> = HashMap()
    val webSocketSessions:MutableMap<String,WebSocketSession> = HashMap()

    val logger = LoggerFactory.getLogger(this.javaClass)



    override fun afterConnectionEstablished(session: WebSocketSession) {
        super.afterConnectionEstablished(session)
        val userName = getUserName(session)
        this.webSocketSessions[userName] = session
    }

    override fun afterConnectionClosed(session: WebSocketSession, status: CloseStatus) {
        super.afterConnectionClosed(session, status)
        val userName = getUserName(session)
        playerInGame[userName]?.let { gameId->
            games[gameId]?.let { executor->
                if(executor.isPlayerInGame(userName)){
                    executor.playerDisconnect(userName)
                }
            }
        }
        this.webSocketSessions.remove(userName)
    }

    override fun handleTextMessage(session: WebSocketSession, message: TextMessage) {

        super.handleTextMessage(session, message)

        val userName = getUserName(session)
        val gameMessage = ClientGameMessage.resolve(JsonUtil.convertToObject(message.payload))
        when(gameMessage){
            //表示玩家连接进一个游戏，需要给玩家发送初始化信息。当玩家人数抵达预期的在线人数时，启动游戏
            is ClientGameMessage.InitMessage ->{
                //发送游戏初始化消息
                games[gameMessage.gameId]?.let { executor->
                    if(executor.isPlayerInGame(userName)){
                        sendInitMessage(executor.getPlayerId(userName),executor.game.getGameData(),session)
                        executor.playerConnect(userName)

                        logger.info("player $userName join in game ${gameMessage.gameId}")
                        //当玩家人数抵达预期的在线人数，启动游戏
                        if(executor.canStartGame()){
                            logger.info("start game ${gameMessage.gameId}")
                            executor.tryStartGame { frame->

                                executor.playerIdMapper.keys.forEach{  _userName->

                                    sendNextSyncFrameToPlayer(_userName,frame)
                                }
                            }
                        }
                    }
                }
            }
            is ClientGameMessage.FrameRequestMessage->{
                games[gameMessage.gameId]?.let{ executor->
                    if(executor.isPlayerInGame(userName)){
                        executor.requestFrame(gameMessage.frameIndices){ frames->
                            sendSyncFramesToPlayer(userName,frames)
                        }
                    }
                }

            }
            is ClientGameMessage.ClientFrameMessage ->{
                games[gameMessage.gameId]?.let { executor->
                    if (executor.isPlayerInGame(userName)) {
                        executor.sync(gameMessage.frameIndex,gameMessage.option,userName)
                    }
                }

            }

        }
    }

    /**
     * @see HandShakeInterceptor
     */
    private fun getUserName(session: WebSocketSession):String{
        return session.attributes["userName"] as String
    }

    /**
     * 发送游戏初始化消息
     */
    private fun sendInitMessage(playerId:Int, gameData: GameData,session: WebSocketSession){
        try {
            val packageInitMessage = ServerGameMessage.packageInitMessage(ServerGameMessage.GameInitMessage(playerId,gameData))
            sendMessageAsJson(packageInitMessage,session)
        }catch (_:Exception){

        }

    }
    private fun sendMessageAsJson(obj:Any, session:WebSocketSession){
        try {
            session.sendMessage(TextMessage(JsonUtil.convertToString(obj)))
        }catch (_:Exception){

        }

    }
    private fun sendNextSyncFrameToPlayer(userName:String, frame:SyncFrameDto){
        try {
            webSocketSessions[userName]?.let{ session->
                val message = ServerGameMessage.packageFrameMessage(ServerGameMessage.SyncFrameMessage(arrayOf(frame)))

                sendMessageAsJson(message,session)
            }
        }catch (_:Exception){

        }

    }
    private fun sendSyncFramesToPlayer(userName:String, frames:Array<SyncFrameDto>){
        try {
            webSocketSessions[userName]?.let{ session->
                val message = ServerGameMessage.packageFrameMessage(ServerGameMessage.SyncFrameMessage(frames))

                sendMessageAsJson(message,session)
            }
        }catch (_:Exception){

        }

    }
    @Synchronized
     fun registerGame(gameExecutor: GameExecutor){
        games[gameExecutor.game.id!!] = gameExecutor
    }
}